use core::{ffi::c_void, ptr::null_mut};

use utils::MutCell;

use crate::{c_src::{osFlagsError, osRtxThread_t, osThreadAttr_t, osThreadDetached, osThreadExit, osThreadFlagsClear, osThreadFlagsSet, osThreadId_t, osThreadJoin, osThreadNew}, osPriority_t, osStatus_t};

use super::StaticMem;



const CB_SIZE: usize = size_of::<osRtxThread_t>();

unsafe impl <const STACK_SIZE: usize> Send for Thread<STACK_SIZE> {}
unsafe impl <const STACK_SIZE: usize> Sync for Thread<STACK_SIZE> {}

pub struct Thread<const STACK_SIZE: usize> {
    id: MutCell<osThreadId_t>,
    cb_mem: StaticMem<CB_SIZE>,
    stack_mem: StaticMem<STACK_SIZE>,
}

pub trait Task {
    const ATTR: u32 = osThreadDetached;
    const PRIORITY: osPriority_t = osPriority_t::osPriorityHigh;
    const NAME: &'static str = "unknown";

    fn run(&self);
}

impl <const STACK_SIZE: usize> Thread<STACK_SIZE> {

    pub const fn uninit() -> Self {
        Self {
            id: MutCell::const_new(null_mut()),
            cb_mem: StaticMem::const_new(),
            stack_mem: StaticMem::const_new(),
        }
    }

    pub fn join(&self) -> osStatus_t {
        let ret = unsafe {
            osThreadJoin(*self.id)
        };
        if ret != osStatus_t::osOK {
            panic!("join fail")
        }
        ret
    }

    pub fn clear_flags(flags: u32) -> u32 {
        let ret = unsafe {
            osThreadFlagsClear(flags)
        };
        if (ret & osFlagsError) != 0x00 {
            panic!("clear flags error");
        }
        ret
    }

    pub fn set_flags(&self, flags: u32) -> u32 {
        let ret = unsafe {
            osThreadFlagsSet(*self.id, flags)
        };
        if (ret & osFlagsError) != 0x00 {
            panic!("set flags error");
        }
        ret
    }

    pub unsafe fn spawn<T: Task>(&self, task: &'static T) {
        let attr = osThreadAttr_t {
            name: T::NAME.as_ptr() as _,
            attr_bits: T::ATTR,
            cb_mem: self.cb_mem.as_ptr(),
            cb_size: CB_SIZE as u32,
            stack_mem: self.stack_mem.as_ptr(),
            stack_size: STACK_SIZE as u32,
            priority: T::PRIORITY,
            tz_module: 0,
            reserved: 0,
        };
        extern "C" fn task_main<T: Task>(args: *mut c_void) {
            let task = unsafe { &*(args as *const T) };
            task.run();
            unsafe { osThreadExit(); }
        }

        let id = osThreadNew(Some(task_main::<T>), task as *const T as _, &attr);
        if id.is_null() {
            panic!("thread new fail");
        }
        *self.id.as_mut() = id;
    }
}






